#ifndef _DEUNIQUEPTR_HPP
#define _DEUNIQUEPTR_HPP
/*-------------------------------------------------------------------------
 * drawElements C++ Base Library
 * -----------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Unique pointer.
 *//*--------------------------------------------------------------------*/

#include "deDefs.hpp"

namespace de
{

//! Unique pointer self-test.
void UniquePtr_selfTest (void);

// Hide implementation-private types in a details namespace.
namespace details
{

//! Auxiliary struct used to pass references between unique pointers. To
//! ensure that managed pointers are deleted exactly once, this type should
//! not appear in user code.
template<typename T, class D>
struct PtrData
{
				PtrData	(T* p, D d) : ptr(p), deleter(d) {}

				template <typename T2, class D2>
				PtrData	(const PtrData<T2, D2>& d) : ptr(d.ptr), deleter(d.deleter) {}

	T*			ptr;
	D			deleter;
};

template<typename T, class D>
class UniqueBase
{
public:
	typedef			T				element_type;
	typedef			D				deleter_type;

	T*				get				(void) const throw() { return m_data.ptr;	}	//!< Get stored pointer.
	D				getDeleter		(void) const throw() { return m_data.deleter; }
	T*				operator->		(void) const throw() { return get();	}	//!< Get stored pointer.
	T&				operator*		(void) const throw() { return *get();	}	//!< De-reference stored pointer.
	operator		bool			(void) const throw() { return !!get();	}

protected:
					UniqueBase		(T* ptr, D deleter)		: m_data(ptr, deleter) {}
					UniqueBase		(PtrData<T, D> data)	: m_data(data) {}
					~UniqueBase		(void);

	void			reset			(void);					//!< Delete previous pointer, set to null.
	PtrData<T, D>	releaseData		(void) throw();			//!< Relinquish ownership, return pointer data.
	void			assignData		(PtrData<T, D> data);	//!< Set new pointer, delete previous pointer.

private:
	PtrData<T, D>	m_data;
};

template <typename T, class D>
UniqueBase<T, D>::~UniqueBase (void)
{
	reset();
}

template <typename T, class D>
void UniqueBase<T, D>::reset (void)
{
	if (m_data.ptr != DE_NULL)
	{
		m_data.deleter(m_data.ptr);
		m_data.ptr = DE_NULL;
	}
}

template <typename T, class D>
PtrData<T, D> UniqueBase<T, D>::releaseData (void) throw()
{
	PtrData<T, D> data = m_data;
	m_data.ptr = DE_NULL;
	return data;
}

template <typename T, class D>
void UniqueBase<T, D>::assignData (PtrData<T, D> data)
{
	if (data.ptr != m_data.ptr)
	{
		reset();
		m_data = data;
	}
}

/*--------------------------------------------------------------------*//*!
 * \brief Movable unique pointer
 *
 * A MovePtr is smart pointer that retains sole ownership of a pointer and
 * destroys it when it is destroyed (for example when it goes out of scope).
 *
 * A MovePtr can be copied and assigned to. The pointer ownership is moved to
 * the newly constructer or assigned-to MovePtr. Upon assignment to a
 * MovePtr, the previously managed pointer is deleted.
 *
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter = DefaultDeleter<T> >
class MovePtr : public UniqueBase<T, Deleter>
{
public:
				MovePtr				(void)									: UniqueBase<T, Deleter> (DE_NULL, Deleter()) {}
	explicit	MovePtr				(T* ptr, Deleter deleter = Deleter())	: UniqueBase<T, Deleter> (ptr, deleter) {}
				MovePtr				(MovePtr<T, Deleter>& other)			: UniqueBase<T, Deleter> (other.releaseData()) {}

	MovePtr&	operator=			(MovePtr<T, Deleter>& other);
	T*			release				(void) throw();
	void		clear				(void) { this->reset(); }

	// These implicit by-value conversions to and from a PtrData are used to
	// allow copying a MovePtr by value when returning from a function. To
	// ensure that the managed pointer gets deleted exactly once, the PtrData
	// should only exist as a temporary conversion step between two MovePtrs.
				MovePtr				(PtrData<T, Deleter> data) : UniqueBase<T, Deleter> (data) {}
	MovePtr&	operator=			(PtrData<T, Deleter> data);

	template<typename U, class Del2>
	operator	PtrData<U, Del2>	(void) { return this->releaseData(); }
};

template<typename T, class D>
MovePtr<T, D>& MovePtr<T,D>::operator= (PtrData<T, D> data)
{
	this->assignData(data);
	return *this;
}

template<typename T, class D>
MovePtr<T, D>& MovePtr<T,D>::operator= (MovePtr<T, D>& other)
{
	return (*this = other.releaseData());
}

//! Steal the managed pointer. The caller is responsible for explicitly
//! deleting the returned pointer.
template<typename T, class D>
inline T* MovePtr<T,D>::release (void) throw()
{
	return this->releaseData().ptr;
}

//! Construct a MovePtr from a pointer.
template<typename T>
inline MovePtr<T> movePtr (T* ptr)					{ return MovePtr<T>(ptr); }

//! Allocate and construct an object and return its address as a MovePtr.
template<typename T>
inline MovePtr<T> newMovePtr (void)					{ return MovePtr<T>(new T()); }
template<typename T, typename P0>
inline MovePtr<T> newMovePtr (P0 p0)				{ return MovePtr<T>(new T(p0)); }
template<typename T, typename P0, typename P1>
inline MovePtr<T> newMovePtr (P0 p0, P1 p1)			{ return MovePtr<T>(new T(p0, p1)); }
template<typename T, typename P0, typename P1, typename P2>
inline MovePtr<T> newMovePtr (P0 p0, P1 p1, P2 p2)	{ return MovePtr<T>(new T(p0, p1, p2)); }

/*--------------------------------------------------------------------*//*!
 * \brief Unique pointer
 *
 * UniquePtr is smart pointer that retains sole ownership of a pointer
 * and destroys it when UniquePtr is destroyed (for example when UniquePtr
 * goes out of scope).
 *
 * UniquePtr is not copyable or assignable. Pointer ownership can be transferred
 * from a UniquePtr only explicitly with the move() member function.
 *
 * A UniquePtr can be constructed from a MovePtr. In this case it assumes
 * ownership of the pointer from the MovePtr. Because a UniquePtr cannot be
 * copied, direct initialization syntax must be used, i.e.:
 *
 *		MovePtr<Foo> createFoo (void);
 *		UniquePtr<Foo> fooPtr(createFoo()); // NOT fooPtr = createFoo();
 *
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter = DefaultDeleter<T> >
class UniquePtr : public UniqueBase<T, Deleter>
{
public:
	explicit				UniquePtr		(T* ptr, Deleter deleter = Deleter());
							UniquePtr		(PtrData<T, Deleter> data);
	MovePtr<T, Deleter>		move			(void);

private:
							UniquePtr		(const UniquePtr<T>& other); // Not allowed!
	UniquePtr				operator=		(const UniquePtr<T>& other); // Not allowed!
};

/*--------------------------------------------------------------------*//*!
 * \brief Construct unique pointer.
 * \param ptr Pointer to be managed.
 *
 * Pointer ownership is transferred to the UniquePtr.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter>
inline UniquePtr<T, Deleter>::UniquePtr (T* ptr, Deleter deleter)
	: UniqueBase<T, Deleter> (ptr, deleter)
{
}

template<typename T, class Deleter>
inline UniquePtr<T, Deleter>::UniquePtr (PtrData<T, Deleter> data)
	: UniqueBase<T, Deleter> (data)
{
}

/*--------------------------------------------------------------------*//*!
 * \brief Relinquish ownership of pointer.
 *
 * This method returns a MovePtr that now owns the pointer. The pointer in
 * the UniquePtr is set to null.
 *//*--------------------------------------------------------------------*/
template<typename T, class Deleter>
inline MovePtr<T, Deleter> UniquePtr<T, Deleter>::move (void)
{
	return MovePtr<T, Deleter>(this->releaseData());
}

} // details

using details::UniquePtr;
using details::MovePtr;
using details::newMovePtr;

} // de

#endif // _DEUNIQUEPTR_HPP
